package chap_02_CAS.step_03_AdderAccumulator;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;

/**
 * @Description synchronized、AtomicLong、LongAdder、LongAccumulator 四种方式的性能比较
 * @Author shihan
 * @Date 2022/10/30 15:50
 * @Version 1.0
 */
public class LongAdderAccumulator_02_4WaysCompare {

    public static final int _5W = 50000;
    public static final int threadNum = 50;

    private long syncNum = 0;

    public synchronized void syncNumAdd() {
        syncNum++;
    }

    private AtomicLong atomicLong = new AtomicLong(0);

    public void atomicLongAdd() {
        atomicLong.incrementAndGet();
    }

    private LongAdder longAdder = new LongAdder();

    public void longAdderAdd() {
        longAdder.increment();
    }

    private LongAccumulator longAccumulator = new LongAccumulator((left, right) -> left + right, 0);

    public void longAccumulatorAdd() {
        longAccumulator.accumulate(1);
    }

    /**
     * 最终结果：
     * LongAccumulator < LongAdder < AtomicLong < synchronized
     * LongAccumulator 约等于 LongAdder，但是 LongAccumulator 性能更好，AtomicLong 中等，synchronized 性能最差；
     *
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        LongAdderAccumulator_02_4WaysCompare waysCompare = new LongAdderAccumulator_02_4WaysCompare();
        long startTime;
        long endTime;
        CountDownLatch countDownLatch1 = new CountDownLatch(50);
        CountDownLatch countDownLatch2 = new CountDownLatch(50);
        CountDownLatch countDownLatch3 = new CountDownLatch(50);
        CountDownLatch countDownLatch4 = new CountDownLatch(50);

        startTime = System.currentTimeMillis();
        for (int i = 0; i < threadNum; i++) {
            new Thread(() -> {
                try {
                    for (int j = 0; j < _5W; j++) {
                        waysCompare.syncNumAdd();
                    }
                } finally {
                    countDownLatch1.countDown();
                }
            }, String.valueOf(i)).start();
        }
        countDownLatch1.await();
        endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime + "\t" + "syncNumAdd:" + waysCompare.syncNum);

        //=====================================================================================

        startTime = System.currentTimeMillis();
        for (int i = 0; i < threadNum; i++) {
            new Thread(() -> {
                try {
                    for (int j = 0; j < _5W; j++) {
                        waysCompare.atomicLongAdd();
                    }
                } finally {
                    countDownLatch2.countDown();
                }
            }, String.valueOf(i)).start();
        }
        countDownLatch2.await();
        endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime + "\t" + "atomicLongAdd:" + waysCompare.atomicLong.get());

        //=====================================================================================

        startTime = System.currentTimeMillis();
        for (int i = 0; i < threadNum; i++) {
            new Thread(() -> {
                try {
                    for (int j = 0; j < _5W; j++) {
                        waysCompare.longAdderAdd();
                    }
                } finally {
                    countDownLatch3.countDown();
                }
            }, String.valueOf(i)).start();
        }
        countDownLatch3.await();
        endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime + "\t" + "longAdderAdd:" + waysCompare.longAdder.sum());

        //=====================================================================================

        startTime = System.currentTimeMillis();
        for (int i = 0; i < threadNum; i++) {
            new Thread(() -> {
                try {
                    for (int j = 0; j < _5W; j++) {
                        waysCompare.longAccumulatorAdd();
                    }
                } finally {
                    countDownLatch4.countDown();
                }
            }, String.valueOf(i)).start();
        }
        countDownLatch4.await();
        endTime = System.currentTimeMillis();
        System.out.println(endTime - startTime + "\t" + "longAccumulatorAdd:" + waysCompare.longAccumulator.get());

    }

}
